﻿///////////////////////////////////////////////////////////////////////////////
// Activity: program.activity0
// Diagram0 service implementation
///////////////////////////////////////////////////////////////////////////////

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Xml;

using ccr = Microsoft.Ccr.Core;
using dss = Microsoft.Dss.Core;
using dssa = Microsoft.Dss.Core.Attributes;
using dssh = Microsoft.Dss.Core.DsspHttp;
using dssm = Microsoft.Dss.ServiceModel.DsspServiceBase;
using dssp = Microsoft.Dss.ServiceModel.Dssp;
using soap = W3C.Soap;

using submgr = Microsoft.Dss.Services.SubscriptionManager;
using log = Microsoft.Robotics.Services.Sample.Log.Proxy;
using drive = Microsoft.Robotics.Services.Drive.Proxy;

namespace Robotics.RoverDrive.Diagram0
{
    [DisplayName("RoverDrive Diagram0")]
    [Description("A user defined activity.")]
    [dssa.Contract(Contract.Identifier)]
    public class Diagram0Service : dssm.DsspServiceBase
    {
        // Service state
        [dssa.InitialStatePartner(Optional = true)]
        private Diagram0State _state;

        // Service operations port
        [dssa.ServicePort("/RoverDrive/Diagram0", AllowMultipleInstances = true)]
        private Diagram0Operations _mainPort = new Diagram0Operations();

        #region Partner services

        [dssa.Partner("SubMgr", Contract = submgr.Contract.Identifier, CreationPolicy = dssa.PartnerCreationPolicy.CreateAlways)]
        private submgr.SubscriptionManagerPort _subMgr = new submgr.SubscriptionManagerPort();

        // Partner: Log, Contract: http://schemas.microsoft.com/robotics/2006/08/log.html
        [dssa.Partner("Log", Contract = log.Contract.Identifier, CreationPolicy = dssa.PartnerCreationPolicy.UsePartnerListEntry)]
        log.LogOperations _logPort = new log.LogOperations();

        // Partner: VEXWheelDrive, Contract: http://schemas.microsoft.com/robotics/2006/05/drive.html
        [dssa.Partner("VEXWheelDrive", Contract = drive.Contract.Identifier, CreationPolicy = dssa.PartnerCreationPolicy.UsePartnerListEntry)]
        drive.DriveOperations _vEXWheelDrivePort = new drive.DriveOperations();
        drive.DriveOperations _vEXWheelDriveNotify = new drive.DriveOperations();

        #endregion

        public Diagram0Service(dssp.DsspServiceCreationPort creationPort)
            : base(creationPort)
        {
        }

        protected override void Start()
        {
            // If there was no initial state partner, then the service state will be null.
            if (_state == null)
            {
                // The state MUST be created before the service starts processing messages.
                _state = new Diagram0State();
            }

            // The rest of the start process requires the ability to wait for responses from
            // services and from the start handler (if any). So execution now proceeds in an
            // iterator function.
            SpawnIterator(DoStart);
        }

        private IEnumerator<ccr.ITask> DoStart()
        {
            soap.Fault fault = null;

            // Subscribe to partners

            yield return ccr.Arbiter.Choice(
                _vEXWheelDrivePort.Subscribe(_vEXWheelDriveNotify,                
                    typeof(Microsoft.Robotics.Services.Drive.Proxy.EnableDrive),
                    typeof(Microsoft.Robotics.Services.Drive.Proxy.RotateDegrees),
                    typeof(Microsoft.Robotics.Services.Drive.Proxy.DriveDistance)
                ),
                EmptyHandler,
                delegate(soap.Fault f)
                {
                    fault = f;
                }
            );

            if (fault != null)
            {
                LogError("Failed to subscribe to program.VEXRoverDrive.VEXWheelDrive", fault);
                StartFailed();
                yield break;
            }

            // Start operation handlers and insert into directory service.
            StartHandlers();

            // Add notifications to the main interleave
            base.MainPortInterleave.CombineWith(
                new ccr.Interleave(
                    new ccr.ExclusiveReceiverGroup(
                    ),
                    new ccr.ConcurrentReceiverGroup(
                    )
                )
            );

            // Activate independent tasks
            Activate<ccr.ITask>(
                ccr.Arbiter.ReceiveWithIterator<Microsoft.Robotics.Services.Drive.Proxy.EnableDrive>(true, _vEXWheelDriveNotify, VEXWheelDriveEnableDriveHandler),
                ccr.Arbiter.ReceiveWithIterator<Microsoft.Robotics.Services.Drive.Proxy.RotateDegrees>(true, _vEXWheelDriveNotify, VEXWheelDriveRotateDegreesHandler),
                ccr.Arbiter.ReceiveWithIterator<Microsoft.Robotics.Services.Drive.Proxy.DriveDistance>(true, _vEXWheelDriveNotify, VEXWheelDriveDriveDistanceHandler)
            );

            yield break;
        }

        private void StartHandlers()
        {
            // Activate message handlers for this service and insert into the directory.
            base.Start();
        }

        #region Standard DSS message handlers

        [dssa.ServiceHandler(dssa.ServiceHandlerBehavior.Concurrent)]
        public virtual IEnumerator<ccr.ITask> GetHandler(Get get)
        {
            get.ResponsePort.Post(_state);
            yield break;
        }

        [dssa.ServiceHandler(dssa.ServiceHandlerBehavior.Concurrent)]
        public virtual IEnumerator<ccr.ITask> HttpGetHandler(dssh.HttpGet httpGet)
        {
            httpGet.ResponsePort.Post(new dssh.HttpResponseType(_state));
            yield break;
        }

        [dssa.ServiceHandler(dssa.ServiceHandlerBehavior.Exclusive)]
        public virtual IEnumerator<ccr.ITask> ReplaceHandler(Replace replace)
        {
            _state = replace.Body;

            replace.ResponsePort.Post(dssp.DefaultReplaceResponseType.Instance);
            base.SendNotification<Replace>(_subMgr, replace);

            yield break;
        }

        [dssa.ServiceHandler(dssa.ServiceHandlerBehavior.Concurrent)]
        public virtual IEnumerator<ccr.ITask> SubscribeHandler(Subscribe subscribe)
        {
            dssp.SubscribeRequestType request = subscribe.Body;

            yield return ccr.Arbiter.Choice(
                SubscribeHelper(_subMgr, request, subscribe.ResponsePort),
                delegate(ccr.SuccessResult success)
                {
                    base.SendNotificationToTarget<Replace>(request.Subscriber, _subMgr, _state);
                },
                delegate(Exception e) { }
            );
        }

        #endregion

        #region Custom message handlers

        [dssa.ServiceHandler(dssa.ServiceHandlerBehavior.Independent)]
        public virtual IEnumerator<ccr.ITask> ActionHandler(Action message)
        {
            // Empty handler. Respond with default response type.
            message.ResponsePort.Post(new ActionResponse());
            yield break;
        }

        #endregion

        #region Notification message handlers

        IEnumerator<ccr.ITask> VEXWheelDriveEnableDriveHandler(Microsoft.Robotics.Services.Drive.Proxy.EnableDrive message)
        {
            OnVEXWheelDriveEnableDriveHandler handler = new OnVEXWheelDriveEnableDriveHandler(this, Environment.TaskQueue);
            return handler.RunHandler(message);
        }

        IEnumerator<ccr.ITask> VEXWheelDriveRotateDegreesHandler(Microsoft.Robotics.Services.Drive.Proxy.RotateDegrees message)
        {
            OnVEXWheelDriveRotateDegreesHandler handler = new OnVEXWheelDriveRotateDegreesHandler(this, Environment.TaskQueue);
            return handler.RunHandler(message);
        }

        IEnumerator<ccr.ITask> VEXWheelDriveDriveDistanceHandler(Microsoft.Robotics.Services.Drive.Proxy.DriveDistance message)
        {
            OnVEXWheelDriveDriveDistanceHandler handler = new OnVEXWheelDriveDriveDistanceHandler(this, Environment.TaskQueue);
            return handler.RunHandler(message);
        }

        #endregion

        #region OnVEXWheelDriveEnableDriveHandler class

        class OnVEXWheelDriveEnableDriveHandler : HandlerBase
        {
            ///////////////////////////////////////////////////////////////////
            // expr - __use__0.snippet0.snippet.noop - [(__use__0.snippet0.snippet.expr0 - __use__0.snippet0.snippet.join)(__use__0.snippet0.snippet.expr - &__use__0.snippet0.snippet.join)] - __use__0.snippet0.call - __use__0.snippet0.call.iftype
            ///////////////////////////////////////////////////////////////////

            public OnVEXWheelDriveEnableDriveHandler(Diagram0Service service, ccr.DispatcherQueue queue)
                : base(service, queue)
            {
            }

            ///////////////////////////////////////////////////////////////////
            // expr - __use__0.snippet0.snippet.noop - [(__use__0.snippet0.snippet.expr0 - __use__0.snippet0.snippet.join)(__use__0.snippet0.snippet.expr - &__use__0.snippet0.snippet.join)] - __use__0.snippet0.call - __use__0.snippet0.call.iftype
            ///////////////////////////////////////////////////////////////////

            public IEnumerator<ccr.ITask> RunHandler(Microsoft.Robotics.Services.Drive.Proxy.EnableDrive message)
            {
                string a = @"Enable Drive " + message.Body.Enable;
                log.LogInfoRequest request = new log.LogInfoRequest();
                request.Category = a;
                request.Message = a;
                LogPort.LogInfo(request);

                base.Complete.Post(ccr.EmptyValue.SharedInstance);
                yield break;
            }

            class JoinAlpha
            {
                public string Message;
                public string Category;

                public JoinAlpha()
                {
                }

                public JoinAlpha(object[] args)
                {
                    Message = args[0].ToString();
                    Category = args[1].ToString();
                }
            }

            ccr.Port<object>[] _joinAlphaPorts = new ccr.Port<object>[2]{
                new ccr.Port<object>(),
                new ccr.Port<object>()
            };
        }

        #endregion

        #region OnVEXWheelDriveRotateDegreesHandler class

        class OnVEXWheelDriveRotateDegreesHandler : HandlerBase
        {
            ///////////////////////////////////////////////////////////////////
            // __use__1.subscribe
            // expr0 - __use__2.snippet0.snippet.noop - [(__use__2.snippet0.snippet.expr0 - __use__2.snippet0.snippet.join)(__use__2.snippet0.snippet.expr - &__use__2.snippet0.snippet.join)] - __use__2.snippet0.call - __use__2.snippet0.call.iftype
            // if if{__use__3.snippet.call - __use__3.snippet.call.iftype}else{}
            ///////////////////////////////////////////////////////////////////

            public OnVEXWheelDriveRotateDegreesHandler(Diagram0Service service, ccr.DispatcherQueue queue)
                : base(service, queue)
            {
            }

            ///////////////////////////////////////////////////////////////////
            // __use__1.subscribe
            // if if{__use__3.snippet.call - __use__3.snippet.call.iftype}else{}
            // expr0 - __use__2.snippet0.snippet.noop - [(__use__2.snippet0.snippet.expr0 - __use__2.snippet0.snippet.join)(__use__2.snippet0.snippet.expr - &__use__2.snippet0.snippet.join)] - __use__2.snippet0.call - __use__2.snippet0.call.iftype
            ///////////////////////////////////////////////////////////////////

            public IEnumerator<ccr.ITask> RunHandler(Microsoft.Robotics.Services.Drive.Proxy.RotateDegrees message)
            {
                Increment();

                if (message.Body.RotateDegreesStage == Microsoft.Robotics.Services.Drive.Proxy.DriveStage.Completed)
                {
                    VEXWheelDrivePort.AllStop(new drive.AllStopRequest());
                }

                string a = @"Rotate Degrees " + message.Body.RotateDegreesStage;
                log.LogInfoRequest request = new log.LogInfoRequest();
                request.Category = a;
                request.Message = a;
                LogPort.LogInfo(request);

                Decrement();

                yield return WaitUntilComplete();
            }

            class JoinAlpha
            {
                public string Message;
                public string Category;

                public JoinAlpha()
                {
                }

                public JoinAlpha(object[] args)
                {
                    Message = args[0].ToString();
                    Category = args[1].ToString();
                }
            }

            ccr.Port<object>[] _joinAlphaPorts = new ccr.Port<object>[2]{
                new ccr.Port<object>(),
                new ccr.Port<object>()
            };
        }

        #endregion

        #region OnVEXWheelDriveDriveDistanceHandler class

        class OnVEXWheelDriveDriveDistanceHandler : HandlerBase
        {
            ///////////////////////////////////////////////////////////////////
            // __use__4.subscribe
            // expr1 - __use__5.snippet0.snippet.noop - [(__use__5.snippet0.snippet.expr0 - __use__5.snippet0.snippet.join)(__use__5.snippet0.snippet.expr - &__use__5.snippet0.snippet.join)] - __use__5.snippet0.call - __use__5.snippet0.call.iftype
            // if0 if{__use__6.snippet.call - __use__6.snippet.call.iftype}else{}
            ///////////////////////////////////////////////////////////////////

            public OnVEXWheelDriveDriveDistanceHandler(Diagram0Service service, ccr.DispatcherQueue queue)
                : base(service, queue)
            {
            }

            ///////////////////////////////////////////////////////////////////
            // __use__4.subscribe
            // if0 if{__use__6.snippet.call - __use__6.snippet.call.iftype}else{}
            // expr1 - __use__5.snippet0.snippet.noop - [(__use__5.snippet0.snippet.expr0 - __use__5.snippet0.snippet.join)(__use__5.snippet0.snippet.expr - &__use__5.snippet0.snippet.join)] - __use__5.snippet0.call - __use__5.snippet0.call.iftype
            ///////////////////////////////////////////////////////////////////

            public IEnumerator<ccr.ITask> RunHandler(Microsoft.Robotics.Services.Drive.Proxy.DriveDistance message)
            {
                Increment();

                if (message.Body.DriveDistanceStage == Microsoft.Robotics.Services.Drive.Proxy.DriveStage.Completed)
                {
                    VEXWheelDrivePort.AllStop(new drive.AllStopRequest());
                }

                string a = @"Drive Distance " + message.Body.DriveDistanceStage;
                log.LogInfoRequest request = new log.LogInfoRequest();
                request.Category = a;
                request.Message = a;
                LogPort.LogInfo(request);

                Decrement();

                yield return WaitUntilComplete();
            }

            class JoinAlpha
            {
                public string Message;
                public string Category;

                public JoinAlpha()
                {
                }

                public JoinAlpha(object[] args)
                {
                    Message = args[0].ToString();
                    Category = args[1].ToString();
                }
            }

            ccr.Port<object>[] _joinAlphaPorts = new ccr.Port<object>[2]{
                new ccr.Port<object>(),
                new ccr.Port<object>()
            };
        }

        #endregion

        #region Handler utility base class

        class HandlerBase : ccr.CcrServiceBase
        {
            ccr.Port<ccr.EmptyValue> _complete = new ccr.Port<ccr.EmptyValue>();
            ccr.Port<ccr.EmptyValue> _shutdown = new ccr.Port<ccr.EmptyValue>();
            List<ccr.Port<object>[]> _joins = new List<ccr.Port<object>[]>();
            Diagram0Service _service;
            int _count;

            public HandlerBase(Diagram0Service service, ccr.DispatcherQueue queue)
                : base(queue)
            {
                _service = service;
            }

            protected Diagram0State State
            {
                get { return _service._state; }
            }

            bool _stateChanged;

            public bool StateChanged
            {
                get { return _stateChanged; }
                set { _stateChanged = value; }
            }

            protected log.LogOperations LogPort
            {
                get { return _service._logPort; }
            }

            protected drive.DriveOperations VEXWheelDrivePort
            {
                get { return _service._vEXWheelDrivePort; }
            }

            public ccr.Port<ccr.EmptyValue> Complete
            {
                get { return _complete; }
            }

            protected void Increment()
            {
                System.Threading.Interlocked.Increment(ref _count);
            }

            protected void Decrement()
            {
                int offset = 0;

                foreach (ccr.Port<object>[] join in _joins)
                {
                    bool complete = true;
                    int joinCount = 0;

                    foreach (ccr.Port<object> sink in join)
                    {
                        if (sink.ItemCount != 0)
                        {
                            joinCount += sink.ItemCount;
                        }
                        else
                        {
                            complete = false;
                        }
                    }
                    if (complete == false)
                    {
                        offset += joinCount;
                    }
                }

                if (System.Threading.Interlocked.Decrement(ref _count) <= offset)
                {
                    _shutdown.Post(ccr.EmptyValue.SharedInstance);
                    _complete.Post(ccr.EmptyValue.SharedInstance);
                }
            }

            protected void Decrement(int count)
            {
                for(int i = 0; i < count; i++)
                {
                    Decrement();
                }
            }

            protected void RegisterJoin(ccr.Port<object>[] join)
            {
                _joins.Add(join);
            }

            protected ccr.ITask WaitUntilComplete()
            {
                return ccr.Arbiter.Receive(false, _shutdown, NullDelegate);
            }

            protected static void NullDelegate(ccr.EmptyValue token)
            {
            }

            protected void SendNotification<T>(object notification)
                where T : dssp.DsspOperation, new()
            {
                _service.SendNotification<T>(_service._subMgr, notification);
            }

            protected void FaultHandler(soap.Fault fault, string msg)
            {
                _service.LogError(null, msg, fault);
            }

            protected static string Stringize(object obj)
            {
                if (obj == null)
                {
                    return string.Empty;
                }
                else
                {
                    return obj.ToString();
                }
            }

            protected void UnhandledResponse(W3C.Soap.Fault fault)
            {
                _service.LogError("Unhandled fault response from partner service", fault);
                Decrement();
            }

            protected void UnhandledResponse<T>(T response)
            {
                Decrement();
            }

            #region Type Cast Functions

            protected static bool BoolCast(object obj)
            {
                if (obj != null && obj is IConvertible)
                {
                    try
                    {
                        return ((IConvertible)obj).ToBoolean(System.Globalization.CultureInfo.InvariantCulture);
                    }
                    catch (FormatException)
                    {
                    }
                }
                return false;
            }

            protected static byte ByteCast(object obj)
            {
                if (obj != null && obj is IConvertible)
                {
                    try
                    {
                        return ((IConvertible)obj).ToByte(System.Globalization.CultureInfo.InvariantCulture);
                    }
                    catch (FormatException)
                    {
                    }
                }
                return 0;
            }

            protected static char CharCast(object obj)
            {
                if (obj != null && obj is IConvertible)
                {
                    try
                    {
                        return ((IConvertible)obj).ToChar(System.Globalization.CultureInfo.InvariantCulture);
                    }
                    catch (FormatException)
                    {
                    }
                }
                return '\0';
            }

            protected static decimal DecimalCast(object obj)
            {
                if (obj != null && obj is IConvertible)
                {
                    try
                    {
                        return ((IConvertible)obj).ToDecimal(System.Globalization.CultureInfo.InvariantCulture);
                    }
                    catch (FormatException)
                    {
                    }
                }
                return 0;
            }

            protected static double DoubleCast(object obj)
            {
                if (obj != null && obj is IConvertible)
                {
                    try
                    {
                        return ((IConvertible)obj).ToDouble(System.Globalization.CultureInfo.InvariantCulture);
                    }
                    catch (FormatException)
                    {
                    }
                }
                return 0;
            }

            protected static float FloatCast(object obj)
            {
                if (obj != null && obj is IConvertible)
                {
                    try
                    {
                        return ((IConvertible)obj).ToSingle(System.Globalization.CultureInfo.InvariantCulture);
                    }
                    catch (FormatException)
                    {
                    }
                }
                return 0;
            }

            protected static int IntCast(object obj)
            {
                if (obj != null && obj is IConvertible)
                {
                    try
                    {
                        return ((IConvertible)obj).ToInt32(System.Globalization.CultureInfo.InvariantCulture);
                    }
                    catch (FormatException)
                    {
                    }
                }
                return 0;
            }

            protected static long LongCast(object obj)
            {
                if (obj != null && obj is IConvertible)
                {
                    try
                    {
                        return ((IConvertible)obj).ToInt64(System.Globalization.CultureInfo.InvariantCulture);
                    }
                    catch (FormatException)
                    {
                    }
                }
                return 0;
            }

            protected static sbyte SByteCast(object obj)
            {
                if (obj != null && obj is IConvertible)
                {
                    try
                    {
                        return ((IConvertible)obj).ToSByte(System.Globalization.CultureInfo.InvariantCulture);
                    }
                    catch (FormatException)
                    {
                    }
                }
                return 0;
            }

            protected static short ShortCast(object obj)
            {
                if (obj != null && obj is IConvertible)
                {
                    try
                    {
                        return ((IConvertible)obj).ToInt16(System.Globalization.CultureInfo.InvariantCulture);
                    }
                    catch (FormatException)
                    {
                    }
                }
                return 0;
            }

            protected static uint UIntCast(object obj)
            {
                if (obj != null && obj is IConvertible)
                {
                    try
                    {
                        return ((IConvertible)obj).ToUInt32(System.Globalization.CultureInfo.InvariantCulture);
                    }
                    catch (FormatException)
                    {
                    }
                }
                return 0;
            }

            protected static ulong ULongCast(object obj)
            {
                if (obj != null && obj is IConvertible)
                {
                    try
                    {
                        return ((IConvertible)obj).ToUInt64(System.Globalization.CultureInfo.InvariantCulture);
                    }
                    catch (FormatException)
                    {
                    }
                }
                return 0;
            }

            protected static ushort UShortCast(object obj)
            {
                if (obj != null && obj is IConvertible)
                {
                    try
                    {
                        return ((IConvertible)obj).ToUInt16(System.Globalization.CultureInfo.InvariantCulture);
                    }
                    catch (FormatException)
                    {
                    }
                }
                return 0;
            }

            #endregion

            #region List Functions

            protected List<T> ListAdd<T>(List<T> list, T item)
            {
                List<T> output = new List<T>(list);
                output.Add(item);
                return output;
            }

            protected List<T> ListConcat<T>(List<T> head, List<T> tail)
            {
                List<T> output = new List<T>(head);
                output.AddRange(tail);
                return output;
            }

            protected int ListIndex<T>(List<T> list, T item)
            {
                return list.IndexOf(item);
            }

            protected List<T> ListRemove<T>(List<T> list, int index)
            {
                List<T> output = new List<T>(list);
                output.RemoveAt(index);
                return output;
            }

            protected List<T> ListReverse<T>(List<T> list)
            {
                List<T> output = new List<T>(list);
                output.Reverse();
                return output;
            }

            protected List<T> ListSort<T>(List<T> list)
            {
                List<T> output = new List<T>(list);
                output.Sort();
                return output;
            }

            protected List<T> ListInsert<T>(List<T> list, T item, int index)
            {
                List<T> output = new List<T>(list);
                output.Insert(index, item);
                return output;
            }

            #endregion
        }

        #endregion
    }
}
